// SPDX-License-Identifier: GPL-2.0
/*
 * TIERED MEMORY: DEMOTION AND PROMOTION
 *
 * (C) 2024.07.15 BuddyZhang1 <buddy.zhang@aliyun.com>
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
/* NUMA header with -lnuma */
#include <numa.h>
#include <numaif.h>

#define MAP_SIZE		(100 * 1024 * 1024) /* 100MiB */
#define PAGE_SIZE		4096
#define MAP_VADDR		(0x6000000000)
#define NR_DEMOT_PAGES		100
#define NB_FREQ			10
#define DEMOTION_NODE		1 /* CXL.MEM ON NUMA NODE 1 */

int main()
{
	int count = NB_FREQ, idx = 0, node;
	void *DemotPages[NR_DEMOT_PAGES];
	void *mem, *base, *end;
	char ch;

	/* ALLOC VIRTUAL MEMORY */
	mem = mmap((void *)MAP_VADDR, MAP_SIZE,
		   PROT_READ | PROT_WRITE,
		   MAP_ANONYMOUS | MAP_PRIVATE,
		   -1,
		   0);
	if (mem == MAP_FAILED) {
		printf("ERROR: ALLOC MEMORY FAILED.\n");
		return -1;
	}
	/* SETUP RANGE */
	base = mem;
	end  = (void *)((unsigned long)mem + MAP_SIZE - PAGE_SIZE);

	/* DEMOTION */
	while ((unsigned long)mem <= (unsigned long)end) {
		/* TRIGGER #PF AND DEMOTION */
		*(char *)mem = 'B';
		/* DETECT NUMA NODE */
		numa_move_pages(0, 1, &mem, NULL, &node, 0);
		/* RECORD DEMOTION PAGE */
		if (node == DEMOTION_NODE && idx < NR_DEMOT_PAGES)
			DemotPages[idx++] = mem;

		/* NEXT PAGE */
		mem = (void *)((unsigned long)mem + PAGE_SIZE);
	}

	/* NUMA BALANCING */
	while (count-- > 0) {
		ch = *(char *)base;
		/* SCHEDULE AND TRIGGER CFS NUMA BALANCING */
		sleep(0.8);
	}

	/* PROMOTION */
	for (idx = 0, count = 0; /* FORCE LOOP*/ ; idx++) {
		/* LOOP ACCESS COLD PAGE */
		if (idx >= NR_DEMOT_PAGES)
			idx = 0;

		mem = DemotPages[idx];
		if (!mem) { /* HOT PAGE */
			sleep(0.1);
			continue;
		}

		/* ACCESS COLD PAGE */
		*(char *)mem = 'D';
		/* DETECT NUMA NODE */
		numa_move_pages(0, 1, &mem, NULL, &node, 0);
		if (node == DEMOTION_NODE)
			printf("%#lx IS COLD PAGE.\n", (unsigned long)mem);
		else {
			printf("%#lx IS HOT PAGE.\n", (unsigned long)mem);
			DemotPages[idx] = NULL;
			/* HOT COUNT */
			if (++count >= NR_DEMOT_PAGES)
				break;
		}
	}

	/* FORBID OPTIONS */
	printf("PROMOTION DONE: %ciscuitOS\n", ch);

	/* RECLAIM */
	munmap(mem, MAP_SIZE);

	return 0;
}
